package com.nari.kmd_dataservice.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.nari.core.tool.utils.RedisUtil;
import com.nari.kmd_dataservice.constant.ConstantParameter;
import com.nari.kmd_dataservice.constant.GlobalVariable;
import com.nari.kmd_dataservice.jwt.PassToken;
import com.nari.kmd_dataservice.jwt.UserLoginToken;
import com.nari.kmd_dataservice.mapper.ServerMapper;
import com.nari.kmd_dataservice.pojo.dto.common.CommonResponseDTO;
import com.nari.kmd_dataservice.pojo.entity.User;
import com.nari.kmd_dataservice.util.GsonUtils;
import com.nari.kmd_dataservice.util.SM3;
import com.nari.kmd_dataservice.util.SM4;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @ClassName: AuthenticationInterceptor
 * @Description: lan jie qi token
 * @Author: LZL
 * @Date: 2022/01/24 14:30
 * @Version 1.0.0
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Resource
    ServerMapper serverMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");// 从 http 请求头中取出 token
        String requestURI = request.getRequestURI();
        // 如果不是映射到方法直接通过
        if(!(handler instanceof HandlerMethod) || !GlobalVariable.checkToken){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)handler;
        Method method=handlerMethod.getMethod();
        //检查是否有passtoken注释，有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        response.reset();
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录1");
                }
                // 验证 token
                DecodedJWT jwt = null;
                try {
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(ConstantParameter.tokenKey)).build();
                    jwt = jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录2");
                }
                // 获取 token 中的 账号信息
                Map<String, Claim> claims = jwt.getClaims();
                Claim claimE = claims.get("e");
                if (claimE==null || claimE.asString()==null) {
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录3");
                }
                String decrypt;
                try {
                    decrypt = SM4.decryptEcb(ConstantParameter.tokenParaSM4Key, claimE.asString());
                } catch (Exception e) {
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录4");
                }
                String userName;
                String password;
                try {
                    JSONObject jsonObject = new JSONObject(decrypt);
                    userName = jsonObject.getString("u");
                    password = jsonObject.getString("p");
                } catch (Exception e) {
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录5");
                }
                log.info("token解密，账号{}，密码{}",userName,password);
                Object redisValue = redisUtil.get(userName);
                if(redisValue==null){
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录6");
                }
                if(!token.equals(redisValue)){
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录7");
                }
                User user = serverMapper.selectUser(userName,password);
                if(user==null){
                    return errorHandle(requestURI, response,"03","tonken无效，请重新登录8");
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    public static boolean errorHandle(String requestURI, HttpServletResponse response, String code, String msg) throws IOException {
        String body = GsonUtils.toJsonWtihNullField(new CommonResponseDTO(code,msg));
        //重置response
        response.reset();
        //设置编码格式
        response.setCharacterEncoding("UTF-8");
//        response.setContentType("application/json;charset=UTF-8");
        PrintWriter pw = response.getWriter();
        if("/server/test".equals(requestURI) || !GlobalVariable.checkSM4){
            String sign = SM3.encrypt(body);
            response.setHeader("sign", sign);
            log.info("明文异常响应sign：{}，参数：{}", sign, body);
            pw.write(body);
        }
        else{
            String encryptBody = SM4.encryptEcb(ConstantParameter.localSM4Key, body);
            String sign = SM3.encrypt(encryptBody);
            response.setHeader("sign", sign);
            log.info("密文异常响应sign：{}，参数：{}，加密{}",sign, body, encryptBody);
            pw.write(encryptBody);
        }
        pw.flush();
        pw.close();
        return false;
    }
}
